<template>
  <canvas id="three"></canvas>

</template>

<script>
import {initThree, initCameraMoveFunction, updateCameraPosition} from '@/utils/enviromentUtil'
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader'
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
import {RGBELoader} from "three/examples/jsm/loaders/RGBELoader";
import {Water} from 'three/examples/jsm/objects/Water2'
import * as THREE from 'three'
import gsap from 'gsap'

const gltfLoader = new GLTFLoader()
// 设置gltf模型加载器的解压器
let dracoLoader = new DRACOLoader();
/**
 * 要求给定解码器的位置
 * 解码器必须从 threeJs/vue-three/node_modules/three/examples/jsm/libs/draco 中完整的拷贝到项目目录中
 * 即从 node_modules 拷贝到工程目录中 才能被访问到
 * 必须使用本地 yarn 安装之后的 node_modules 中的 解码器
 * 不然会解码失败，匹配不成功
 */
dracoLoader.setDecoderPath('/static/merryChristmas/draco/')
gltfLoader.setDRACOLoader(dracoLoader)
const pointLightCenterPositionX = -5
const pointLightCenterPositionY = 3
const pointLightCenterPositionZ = 0
const radius = 2
export default {
  name: "merryChristmas",
  data() {
    return {
      scene: '',
      renderer: "",
      camera: "",
      orbitControls: "",

      moveForward: false,
      moveBackward: false,
      moveLeft: false,
      moveRight: false,
      pointLightGroup: ''
    }
  },
  async mounted() {
    initThree.call(this, -3.23, 2.98, 4.06)
    initCameraMoveFunction.call(this)
    this.addLight()

    let texture = this.loadTexture()

    let scene = this.loadModels()

    // 设置色调映射
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping

    this.renderer.outputEncoding = THREE.sRGBEncoding

    // 渲染器启用阴影
    this.renderer.shadowMap.enabled = true

    this.createOcean()

    this.addPointLights()

    this.addStartsInTheSky()

    await Promise.all([texture, scene])

    this.playAnimation()

    this.initLocationsWhereCameraCanBePlaced()


  },

  methods: {
    loadModels() {
      return new Promise((resolve, reject) => {
        gltfLoader.load("static/merryChristmas/model/scene.glb", model => {

          let scene = model.scene;

          scene.traverse(child => {
            if (child.name === 'Plane') {
              // 把模型中的水平面给隐藏起来
              child.visible = false
              return
            }
            if (child.isMesh) {
              // 允许物体接受阴影
              child.receiveShadow = true
              child.castShadow = true
            }
          })

          this.scene.add(scene)
          resolve(scene)
        }, progress => {
        }, error => {
          reject(error)
        })
      })

    },

    addLight() {
      // 添加室内的灯
      let roomLight = new THREE.DirectionalLight(0xffffff, 1)
      roomLight.position.set(0, 50, 0)
      this.scene.add(roomLight)


      let pointLightGroup = new THREE.Group()

      // 添加三个点光源
      let sphereGeometry = new THREE.SphereGeometry(0.1);
      let meshBasicMaterial = new THREE.MeshBasicMaterial({color: 0xFFDF4F});
      let pointLight = new THREE.PointLight(0xFFDF4F, 10);
      for (let i = 0; i < 3; i++) {


        let sphere = new THREE.Mesh(sphereGeometry, meshBasicMaterial)


        sphere.add(pointLight)

        let degree = 2 * Math.PI * i / 3

        let xOffset = radius * Math.cos(degree)

        let zOffset = radius * Math.sin(degree)

        /*        let positionX = pointLightCenterPositionX + xOffset
                let positionY = pointLightCenterPositionY
                let positionZ = pointLightCenterPositionZ + zOffset

                */
        sphere._originalDegree = degree

        sphere.position.set(xOffset, 0, zOffset)

        pointLightGroup.add(sphere)
      }

      this.pointLightGroup = pointLightGroup

      // 把三个点光源合成一个组，这样就不用每次刷新时 每个点光源都要加上起点的位置了
      // 这个组内的所有点光源都是依赖于这个组的位置
      this.pointLightGroup.position.set(pointLightCenterPositionX, pointLightCenterPositionY, pointLightCenterPositionZ)

      this.scene.add(pointLightGroup)
    },

    createOcean() {
      // 创建海洋，使用原型几何来代替海洋
      let circleGeometry = new THREE.CircleGeometry(100, 16)

      let water = new Water(circleGeometry, {
        color: 0xeeffff,
        scale: 20,
        flowDirection: new THREE.Vector2(15, 24),
        textureWidth: 1024,
        textureHeight: 1024
      });

      // water.receiveShadow = true

      water.rotateX(-Math.PI / 2)
      water.position.y -= 2
      this.scene.add(water)
    },

    loadTexture() {
      let rgbeLoader = new RGBELoader();
      return new Promise((resolve, reject) => {
        rgbeLoader.load("static/merryChristmas/textures/sky.hdr", texture => {
          // 因为天空纹理是球型的，这里需要设置
          texture.mapping = THREE.EquirectangularReflectionMapping
          this.scene.background = texture
          this.scene.environment = texture
          resolve(texture)
        })
      })


    },


    addPointLights() {
      let pointLight = new THREE.PointLight(0xddffff, 10);

      pointLight.position.set(0.1, 2.3, 0)
      // 点光源投射阴影
      pointLight.castShadow = true

      this.scene.add(pointLight)
    },

    playAnimation() {

      let clock = new THREE.Clock()

      let degreeSpeed = 2

      let animate = () => {

        this.renderer.render(this.scene, this.camera)

        this.orbitControls.update()

        // 自上一次已过去多少秒
        let pastedSeconds = clock.getElapsedTime()

        let degree = degreeSpeed * pastedSeconds

        this.pointLightGroup.children.forEach(mesh => {
          mesh.position.set(
            radius * Math.cos(degree + mesh._originalDegree),
            0.5 * Math.sin(degree),
            radius * Math.sin(degree + mesh._originalDegree)
          )
        })

        updateCameraPosition.call(this)

        requestAnimationFrame(animate)
      }
      animate()
    },

    initLocationsWhereCameraCanBePlaced(){

      // let timeline1 = gsap.timeline();
      //
      // let timeline2 = gsap.timeline();

      let move = (newPosition,newTarget)=>{

        gsap.to(this.camera.position,{
          x:newPosition.x,
          y:newPosition.y,
          z:newPosition.z,
        })

        gsap.to(this.camera.target,{
          x:newTarget.x,
          y:newTarget.y,
          z:newTarget.z,
        })

        this.camera.updateProjectionMatrix()

      }

      let locations = [
        {
          position: new THREE.Vector3(-3.23,3,4.06),
          target: new THREE.Vector3(-8,2,0)
        },
        {
          position: new THREE.Vector3(7,0,23),
          target: new THREE.Vector3(0,0,0)
        },
        {
          position: new THREE.Vector3(10,3,0),
          target: new THREE.Vector3(5,2,0)
        }
      ]

      let current=0
      window.addEventListener('click',e=>{
        current = ++current % locations.length
        let obj = locations[current]
        move(obj.position,obj.target)

      })


    },

    addStartsInTheSky(){

      let sphereGeometry = new THREE.SphereGeometry(0.1);
      let meshBasicMaterial = new THREE.MeshBasicMaterial({color: 0xFFDF4F});
      let pointLight = new THREE.PointLight(0xFFDF4F,100);

      pointLight.castShadow = true

      // let stars = new THREE.InstancedMesh(sphereGeometry,meshBasicMaterial,1000);

      let starGroup = new THREE.Group();



      for (let i = 0; i < 100; i++) {

        let star = new THREE.Mesh(sphereGeometry,meshBasicMaterial)

        star.add(pointLight)

        star.position.set(
          (Math.random()*2-1)*30,
          (Math.random()*2-1)*30+50,
          (Math.random()*2-1)*30
        )

        starGroup.add(star)


      }
      this.scene.add(starGroup)
    },
  }
}
</script>

<style scoped>

</style>
